#include "const/oPreDefine.h"
#include "model/oModel.h"
#include "model/oModelDef.h"
#include "model/oActionInterval.h"
#include "model/oActionInstant.h"

void oLook::add( CCNode* node )
{
	_nodes.push_back(node);
}

void oLook::apply()
{
	BOOST_FOREACH(CCNode* node, _nodes)
	{
		node->setIsVisible(false);
	}
}

void oLook::unApply()
{
	BOOST_FOREACH(CCNode* node, _nodes)
	{
		node->setIsVisible(true);
	}
}

oModel::oModel( oModelDef* def ):
_isPlaying(false),
_isPaused(false),
_loop(false),
_currentLook(oLook::None),
_currentAnimation(oAnimation::None),
_pModelDef(def),
_speed(1.0f),
_recoverTime(0)
{
	_pModelDef->retain();
	_resetAnimation.end = delegate<void()>(&oModel::onResetAnimationEnd, this);
}

oModel::~oModel()
{
	_pModelDef->release();
	_pModelDef = nullptr;
}

void oModel::addLook( uint32 index, CCNode* node )
{
	for (uint32 n = _looks.size();n < index + 1;_looks.push_back(new oLook()), n++);
	_looks[index].add(node);
}

void oModel::setLook( int index )
{
	if (index < oLook::None || index >= (int)_looks.size())
	{
		if (_looks.empty() && oLook::None < index && _currentLook == oLook::None)
		{
			_currentLook = index;
			CCObject* child;
			CCARRAY_FOREACH(this->getChildren(), child)
			{
				CCNode* node = (CCNode*)child;
				node->setIsVisible(true);
			}
		}
		return;
	}
	if (_currentLook != index)
	{
		if (_currentLook != oLook::None)
		{
			_looks[_currentLook].unApply();
		}
		else
		{
			CCObject* child;
			CCARRAY_FOREACH(this->getChildren(), child)
			{
				CCNode* node = (CCNode*)child;
				node->setIsVisible(true);
			}
		}
		_currentLook = index;
		if (_currentLook != oLook::None)
		{
			_looks[_currentLook].apply();
		}
		else
		{
			CCObject* child;
			CCARRAY_FOREACH(this->getChildren(), child)
			{
				CCNode* node = (CCNode*)child;
				node->setIsVisible(false);
			}
		}
	}
}

void oModel::setLook( const string& name )
{
	int index = _pModelDef->getLookIndexByName(name);
	oModel::setLook(index);
}

int oModel::getLook() const
{
	return _currentLook;
}

void oModel::play( uint32 index )
{
	if (index >= _animationGroups.size())
	{
		return;
	}
	oModel::stop();
	_isPlaying = true;
	_currentAnimation = index;
	if (_recoverTime > 0.0f)
	{
		_isRecovering = true;
		_resetAnimation.run(_recoverTime);
	}
	else
	{
		oModel::reset(_pModelDef->getRoot(), this);
		oModel::onResetAnimationEnd();
	}
}

void oModel::play( const string& name )
{
	int index = _pModelDef->getActionIndexByName(name);
	oModel::play(index);
}

void oModel::addAnimation( uint32 index, CCNode* node, oSequence* action )
{
	for (uint32 n = _animationGroups.size();n < index + 1;_animationGroups.push_back(new ptr_vector<oAnimation>()), n++);
	if (_animationGroups[index].size() == 0)
	{
		action = oSequence::actionOneTwo(
			action,
			oCallFunc::actionWithTarget(this, callfunc_selector(oModel::onActionEnd)));
	}
	_animationGroups[index].push_back(new oAnimation(node, action));
}

void oModel::stop()
{
	_isPlaying = false;
	oModel::resume();
	if (_isRecovering)
	{
		_resetAnimation.stop();
	}
	else
	{
		if (_currentAnimation != oAnimation::None)
		{
			BOOST_FOREACH(oAnimation& pair, _animationGroups[_currentAnimation])
			{
				pair.stop();
			}
		}
	}
}

bool oModel::isPlaying() const
{
	return _isPlaying;
}

void oModel::reset( oSpriteDef* parentDef, CCNode* parentNode )
{
	CCArray* children = parentNode->getChildren();
	if (children)
	{
		const vector<oSpriteDef*>& childrenDef = parentDef->children();
		for (uint32 n = 0;n < children->count();n++)
		{
			CCNode* childNode = (CCNode*)children->objectAtIndex(n);
			childrenDef[n]->restore((CCSprite*)childNode);
			oModel::reset(childrenDef[n], childNode);
		}
	}
}

void oModel::onActionEnd()
{
	if (_loop)
	{
		BOOST_FOREACH(oAnimation& pair, _animationGroups[_currentAnimation])
		{
			pair.stop();
			pair.run();
		}
	}
	else
	{
		_isPlaying = false;
	}
	if (animationEnd != NULL)
	{
		animationEnd(this, _currentAnimation);
	}
}

void oModel::setLoop( bool loop )
{
	_loop = loop;
}

bool oModel::getLoop() const
{
	return _loop;
}

int oModel::getCurrentAnimationIndex() const
{
	if (_isPlaying)
	{
		return _currentAnimation;
	}
	return oAnimation::None;
}

void oModel::resume( uint32 index )
{
	oModel::resume();
	if (_currentAnimation != index)
	{
		oModel::play(index);
	}
}

void oModel::resume( const string& name )
{
	int index = _pModelDef->getActionIndexByName(name);
	oModel::resume(index);
}

void oModel::resume()
{
	if (_isPaused)
	{
		_isPaused = false;
		if (!_isRecovering)
		{
			BOOST_FOREACH(oAnimation& pair, _animationGroups[_currentAnimation])
			{
				pair.resume();
			}
		}
	}
}

oModelDef* oModel::getModelDef() const
{
	return _pModelDef;
}

bool oModel::isPaused() const
{
	return _isPaused;
}

void oModel::pause()
{
	if (_isPlaying && !_isPaused)
	{
		_isPaused = true;
		BOOST_FOREACH(oAnimation& pair, _animationGroups[_currentAnimation])
		{
			pair.pause();
		}
	}
}

void oModel::setSpeed( float speed )
{
	if (_speed != speed)
	{
		_speed = speed;
		for (uint32 n = 0;n < _animationGroups.size();n++)
		{
			BOOST_FOREACH(oAnimation& pair, _animationGroups[n])
			{
				pair.setSpeed(speed);
			}
		}
	}
}

float oModel::getSpeed() const
{
	return _speed;
}

void oModel::setTime( ccTime time )
{
	if (_isPlaying)
	{
		BOOST_FOREACH(oAnimation& pair, _animationGroups[_currentAnimation])
		{
			pair.setTime(time);
		}
	}
}

ccTime oModel::getTime() const
{
	if (_isPlaying)
	{
		if (!_animationGroups[_currentAnimation].empty())
		{
			return _animationGroups[_currentAnimation][0].getTime();
		}
	}
	return 0;
}

void oModel::setRecovery(float var)
{
	if (var < 0.0f)
	{
		var = 0.0f;
	}
	_recoverTime = var;
}

float oModel::getRecovery() const
{
	return _recoverTime;
}

ccTime oModel::getDuration() const
{
	if (_currentAnimation != oAnimation::None)
	{
		return _animationGroups[_currentAnimation][0].getDuration();
	}
	return 0;
}

oModel* oModel::none()
{
	oModelDef* def = new oModelDef();
	def->autorelease();
	oModel* model = new oModel(def);
	model->autorelease();
	return model;
}

void oModel::onResetAnimationEnd()
{
	_isRecovering = false;
	BOOST_FOREACH(oAnimation& pair, _animationGroups[_currentAnimation])
	{
		pair.run();
	}
}

oAnimation::oAnimation( CCNode* node, oSequence* action ):
_node(node),
_action(action)
{
	_action->retain();
}

oAnimation::~oAnimation()
{
	oAnimation::stop();
	_action->release();
	_action = nullptr;
}

void oAnimation::run()
{
	_node->runAction(_action);
}

void oAnimation::stop()
{
	_node->stopAction(_action);
}

CCNode* oAnimation::getNode() const
{
	return _node;
}

oSequence* oAnimation::getAction()
{
	return _action;
}

void oAnimation::pause()
{
	_action->pause();
}

void oAnimation::resume()
{
	_action->resume();
}

void oAnimation::setSpeed( float speed )
{
	_action->setSpeed(speed);
}

float oAnimation::getSpeed() const
{
	return _action->getSpeed();
}

void oAnimation::setTime( ccTime time )
{
	_action->setTime(time);
}

ccTime oAnimation::getTime() const
{
	return _action->getTime();
}

ccTime oAnimation::getDuration() const
{
	return _action->getDuration();
}

void oResetAnimation::add( CCNode* node, CCFiniteTimeAction* action )
{
	_group.push_back(new oAnimationPair(node, action));
	if (_group.size() == 1)
	{
		_group[0].action->release();
		_group[0].action = oSequence::actionOneTwo(
			_group[0].action,
			oCallFunc::actionWithTarget(this, callfunc_selector(oResetAnimation::onActionEnd))
		);
		_group[0].action->retain();
	}
}

void oResetAnimation::run(ccTime duration)
{
	auto it = _group.begin();
	it->node->stopAllActions();
	oSequence* seq = (oSequence*)(it->action);
	seq->one()->setDuration(duration);
	seq->setDuration(duration);
	it->node->runAction(it->action);
	it++;
	for(;it != _group.end();it++)
	{
		it->node->stopAllActions();
		it->action->setDuration(duration);
		it->node->runAction(it->action);
	}
}

void oResetAnimation::stop()
{
	BOOST_FOREACH(oAnimationPair& pair, _group)
	{
		pair.node->stopAction(pair.action);
	}
}

void oResetAnimation::onActionEnd()
{
	if (end != NULL)
	{
		end();
	}
}

oResetAnimation::oAnimationPair::oAnimationPair( CCNode* node, CCFiniteTimeAction* action ):
node(node),action(action)
{
	action->retain();
}

oResetAnimation::oAnimationPair::~oAnimationPair()
{
	action->release();
}
